Skip to content

Reduce Async FrameBuffer memory usage with PackedWrappedBuffer and static offsets - #109

Open
JasperGeurtz wants to merge 6 commits into
developfrom
packed-frame-buffer
Open

JasperGeurtz wants to merge 6 commits into
developfrom
packed-frame-buffer

Conversation

@JasperGeurtz

@JasperGeurtz JasperGeurtz commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

#90

  • Reduce async FrameBuffer memory from 33.0 MB to 4.08 MB per slot (-88%, saving ~290 MB for 10 slots) (Reduce Async FrameBuffer memory usage #90):
    • Route static map/region reads (groundHeight, isWalkable, isBuildable, regions) directly to the main shared memory buffer via AsyncClientData.
    • Omit client-to-server output buffers (strings, shapes, commands, unitCommands) from FrameBuffer (flushed directly to liveGameData via SideEffectQueue).
    • Pack the remaining 3 dynamic ranges into PackedWrappedBuffer (4.08 MB) using pre-shifted slice2 and slice3 buffer views so generated AsyncClientData methods use compile-time BWAPI offsets for both unpacked liveData and packed FrameBuffer slots with zero runtime branches.
  • Only copy active units, events, and eventStrings per frame:
    • Track maxUnitCount (via initialUnitCount and UnitDiscover events) and skip copying the unused tails of units[10000], events[10000], and eventStrings[1000].
    • Reduces per-frame copy size from 4.80 MB (and 330 MB on Frame 0) to ~450 KB at game start, ~685 KB in mid-game, and ~1.19 MB in late-game (~0.05–0.20 ms on normal hardware).
  • Make asyncUnsafe thread-safe on on-time frames:
    • Defer swapping the bot from liveData onto frameBuffer.peek() until the frame actually times out (remainingNanos <= 0) and control is about to return to StarCraft, avoiding mid-frame buffer swaps when the bot finishes on time.
  • Consolidate async locks and eliminate spurious wakeups:
    • Consolidate lockWrite, synchronized framesBuffered(), unsafeReadReadyLock, and lastBotThrowLock onto frameBuffer.lockSize (and volatile lastBotThrow), and stop signalAll() when clearing unsafeReadReady = false.
  • Double-buffer SideEffectQueue:
    • Swap queue and flushQueue under the monitor and apply side effects to liveGameData outside the lock so the bot thread is not blocked on enqueue() during shared-memory flushes.

@JasperGeurtz
JasperGeurtz force-pushed the packed-frame-buffer branch 8 times, most recently from 10dd342 to 236efdd Compare November 7, 2026 15:51
@JasperGeurtz
JasperGeurtz requested a review from dgant November 7, 2026 17:07
Jasper Geurtz added 5 commits November 8, 2026 10:31
…atic offsets (#90)

- Add PackedWrappedBuffer with pre-shifted slice views (slice2, slice3, slice4) so all BWAPI field offsets remain static compile-time constants with zero runtime branches
- Keep original WrappedBuffer pure, only adding a package-private view constructor
- Auto-generate AsyncClientData and slice-routed struct views (AsyncPosition, AsyncEvent, AsyncCommand, AsyncUnitCommand) via DumpToClient
- Preserve unsafe async handoffs across live and packed buffers for both in-flight reads and retained struct views
- Add unit and synchronization tests covering memory reduction, static/dynamic routing, and unsafe async handoffs

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant